package com.toutiao.nssue

import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.{HashMap => HMap}

import java.util.Date

import org.apache.log4j.Logger

import com.toutiao.AppProp

import com.toutiao.issue.DocTitleStore
import com.toutiao.issue.DocTitle

import com.toutiao.score.Scorer
import com.toutiao.score.Measure

import com.toutiao.score.Topic

import com.toutiao.issue.SimpleDate

import com.toutiao.model.{TimeDB,TimeDict,TimeIndex}

class NssTitleModel (
        scorer: Scorer,
        source: DocTitleStore,
		es: TopicStore,
		minV:Double,minS:Double,
		locationDate:String,
		N:Int, // 天
		DB_ROOT:String,
		DB_CACHE:Int
    ) {
  
  private
  val _log = Logger.getLogger(this.getClass.getName)

  private
  val M1 = new HMap[String,Long] // Name -> ID
  private
  val M2 = new HMap[Long,String] // ID -> Name
  private
  var N1 = 0
  private
  var N2 = 0
  
  //private
  //val X = new HMap[String,String] // News Name -> Sim Name 
  
  val Data = source
  
  val mDB = new TimeDB(DB_ROOT,DB_CACHE)
  val mTime = new TimeDict
  val K = 12
  val mIndex = new TimeIndex(K)
  
  val mData = new NssTitleCollector(minV,mIndex,mTime,mDB)
  _log.info("Time Day Aware DB - Title")
  
  val ES = es
  
  def conv(m:DocTitle) = {
    val name = m.name
    val title = m.title
    val image = m.image
    val t = m.date
    val vs = scorer.score(title)
    new Document(name,title,image,vs,t)
  }
  
//  def processMutOne(m:Document) = synchronized {
//    if (!M1.contains(m.name)){
//      N2 += 1
//      M1(m.name) = N2
//      M2(N2) = m.name
//    }
//    
//    val rs = mData.processMut(M1,M2,X,m,minS)
//    // 更新X
//    for (r <- rs) {
//      val ms = r.ms
//      val name = r.name
//      for (n <- ms) {
//        X(n) = name
//      }
//    }
//   
//    rs.map(r => r.name)
//  }
  
  
  def processOne(m:Document) = synchronized {
    if (!M1.contains(m.name)){
      N2 += 1
      M1(m.name) = N2
      M2(N2) = m.name
      
      val r = mData.process(M1,M2,m,minS)
      r
    }
    else {
      //val r = mData.process(M1,M2,m,minS)
      //r
      ""
    }
  }
  
  // Xs是改动,Ys是要删除的
//  def processMut(ms:Array[Document]):(Array[String],Array[String]) = {
//    
//    val xs = ArrayBuffer[String]()
//    val ys = ArrayBuffer[String]()
//    for (m <- ms) {
//      val rs = processMutOne(m)
//      xs ++= rs
//      if (rs.size > 1) {
//        ys.append(m.name)
//      }
//    }
//    
//    (xs.toArray,ys.toArray)
//    
////    val x = Array[String]()
////    val y = Array[String]()
////    (x,y)
//  }
  
  def process(ms:Array[Document]):Array[String] = {
    val ns = ms.map(e => processOne(e)).filter(_ != "")
    ns
  }
  
  def getMaxTime(ms:Array[Document],t:Date) = {
    if (ms.size > 0){
      _getMaxTime(ms)
    }
    else {
      t
    }
  }
  
  def _getMaxTime(ms:Array[Document]) = {
    var t = ms(0).date
    for (m <- ms){
      if (m.date.getTime > t.getTime)
        t = m.date
    }
    t
  }  
  
  def saveOne(n:String) {
    val m = mData.get(n)
    if (m.i != ""){
      ES.insert(m)
    }
  }
  
  def save(ns:Array[String]) {
    val ms = ns.map(mData.get(_)).filter(_.i != "")
    ES.insert(ms)
  }
  
  def remove(T:Date) = {
    val t = SimpleDate.format(T).split(" ")(0)
    if (mDB.isIn(t)){
      val us = mDB(t).getKeys.filter(M1.contains(_)) // !!!
      mData.remove(t)
      //mData.remove(t)
      us.foreach(u => {
        val i = M1(u)
        M1 -= u
        M2 -= i
      })
      N1 += us.size
      
      us.size
    } 
    else {
      0
    }
  }
  
  private
  def _init(xs:Array[Topic]) = {
    for (x <- xs if x.m("m").size > 0) {
      val name = x.i
      N2 += 1
      M1(name) = N2
      M2(N2) = name
      val t = SimpleDate.format(x.c).split(" ")(0)
      mTime.insert(name,t)
      mIndex.insert(t, M1(name), x.m("t"))
      
    }
  }

  def init = {
    val current = SimpleDate.loadDate(locationDate)
    val T1 = SimpleDate.increment(current, -N*60)
    val T2 = SimpleDate.increment(current, 60)
    
    var ct = T1
    
    while (ct.getTime < T2.getTime){
      val t = SimpleDate.format(ct).split(" ")(0)
      _log.info(t)
      mDB.init(t)
      val xs = mDB(t).getData
      _log.info("Got "+xs.size)
      _init(xs)
      _log.info("Build Index Done")
      
      ct = SimpleDate.increment(ct, 60*24)
    }
    
  }
  
  def run(m:Int) = synchronized {
    var current = SimpleDate.loadDate(locationDate)
    val rt = "TIME"
    var eT = Data.getMaxTime(rt)
    
    while (1 == 1) {
      _log.info(SimpleDate.format(eT))
      //val x = SimpleDate.increment(current, m)
      val x = SimpleDate.sIncrement(current, m)
      if (x.getTime <= eT.getTime) {
        _log.info("Current Date "+SimpleDate.format(current))
        _log.info("End Date "+SimpleDate.format(eT))
        val T1 = current
        //val T2 = SimpleDate.increment(T1, m)
        val T2 = SimpleDate.sIncrement(T1, m)
        _log.info(SimpleDate.format(T2))
        
        val ms = Data.retrieve(T1,T2).map(conv)
        val mMaxTime = getMaxTime(ms,T2)
        _log.info("Process ... "+ms.size)
        val ns = process(ms)
        _log.info("Process Done")
        
        _log.info("New Event "+ns.size)
        _log.info("Save ... "+ns.size)
        if (ns.size > 0)
          save(ns)
        _log.info("Save Done")
        
        val T = SimpleDate.increment(current, -60*N)
       
        _log.info(SimpleDate.format(T))
       
        val n = remove(T)
        
        _log.info("Remove News "+n)
        
        _log.info("Current Doc Size "+M1.size)
        _log.info("MAX_ID "+N2)
        _log.info("MIN_ID "+N1)
        val now = SimpleDate.current
        
        current = 
          if (mMaxTime.getTime > current.getTime) 
            if (mMaxTime.getTime <= now.getTime)
              mMaxTime
            else
              now
          else T2
          
        SimpleDate.saveDate(current,locationDate)  
      }
      else {
        val T = SimpleDate.current
        _log.info("Sleep Here "+SimpleDate.format(T))
        Thread.sleep(5*1000)
        eT = Data.getMaxTime(rt)        
      }
      
    }    
  }
  
}

object NssTitleProcess {
  
  def run(prop:AppProp) {
    
    val source = new DocTitleStore(
    					prop("ES_DOC_NAME"),
    					prop("ES_DOC_URI"),
    					prop("ES_DOC_PORT").toInt,
    					prop("ES_DOC_INDEX"),
    					prop("ES_DOC_TYPE"))
    val es = new TopicStore(
    					prop("ES_TOPIC_NAME"),
    					prop("ES_TOPIC_URI"),
    					prop("ES_TOPIC_PORT").toInt,
    					prop("ES_TOPIC_INDEX"),
    					prop("ES_TOPIC_TYPE"))
    
    val locationDate = prop("LOCATION_DATE")
    val N = prop("TIME_SIZE").toInt
    val minV = prop("MIN_V").toDouble
    val minS = prop("MIN_S").toDouble
 
    val dict = Measure.get(prop("LOCATION_MEASURE"))
    val n = prop("DOC_NUM").toInt
    val scorer = new Scorer(dict,n)
    
    val DB_ROOT = prop("DB_ROOT")
    val DB_CACHE = prop("DB_CACHE").toInt
    
    val m = new NssTitleModel(scorer,source,es,minV,minS,locationDate,N,
    					DB_ROOT,DB_CACHE)
    
    m.init
    val K = prop("TIME_STEP").toInt
    m.run(K)
    
  }
  
}

